package cn.edu.hitsz.compiler.lexer;

import cn.edu.hitsz.compiler.NotImplementedException;
import cn.edu.hitsz.compiler.symtab.SymbolTable;
import cn.edu.hitsz.compiler.utils.FilePathConfig;
import cn.edu.hitsz.compiler.utils.FileUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.stream.StreamSupport;

/**
 * TODO: 实验一: 实现词法分析
 * <br>
 * 你可能需要参考的框架代码如下:
 *
 * @see Token 词法单元的实现
 * @see TokenKind 词法单元类型的实现
 */
public class LexicalAnalyzer {
    // 符号表 tokens集 源码阅读器 DFA状态机 文件路径
    private final SymbolTable symbolTable;
    private ArrayList<Token>  Tokens;
    private CodeReader reader;
    private DFA dfa;
    private String filepath;


    public LexicalAnalyzer(SymbolTable symbolTable) {
        // 初始化符号表和Tokens列表
        this.Tokens = new ArrayList<Token>();
        this.symbolTable = symbolTable;
        this.reader = null;
        this.dfa = new DFA(State.START);
    }

    /**
     * 从给予的路径中读取并加载文件内容
     *
     * @param path 路径
     */
    public void loadFile(String path) {
        this.filepath = path;
        // 将文本内容作为字符串完整读入
        this.reader = new CodeReader(FileUtils.readFile(path).toCharArray());

    }

    // 分析单词
    public void read_word(){
        // 如果读取源码失败
        if(this.reader == null){
            throw new RuntimeException("Can not read the source code!");
        }
        String word = "";
        // 状态机分析
        while (this.reader.hasNext()){
            switch (dfa.getCur_state()){
                case START -> {
                    //  最开始肯定是接受了字母
                    String cur_char = String.valueOf(this.reader.next());
                    word += cur_char;
                    dfa.transition(State.WORD);
                }

                case WORD -> {
                    String cur_char = String.valueOf(this.reader.next());
                    // 如果当前读入的字符是字母数字串
                    if(cur_char.matches("[a-zA-Z0-9]")){
                        word += cur_char;
                    }
                    // 如果当前读入的不是字母 则状态结束
                    else{
                        dfa.transition(State.START);
                        // 关键字
                        if(word.equals("int") || word.equals("return")){
                            Tokens.add(Token.simple(word));
                        }
                        // 标识符
                        else {
                            Tokens.add(Token.normal("id",word));
                            // 添加新符号进入符号表
                            if(!symbolTable.has(word)){
                                symbolTable.add(word);
                            }
                        }

                        // 回溯上一个字符
                        this.reader.back();

                        return;
                    }
                }

                default ->  throw new RuntimeException("An unknown state was encountered in DFA!");
            }
        }
    }

    /**
     * 执行词法分析, 准备好用于返回的 token 列表 <br>
     * 需要维护实验一所需的符号表条目, 而得在语法分析中才能确定的符号表条目的成员可以先设置为 null
     */
    public void run() {
        // 如果读取源码失败
        if(this.reader == null){
            throw new RuntimeException("Can not read the source code!");
        }

        // 状态机分析
        while (this.reader.hasNext()){
            String cur_char = String.valueOf(this.reader.next());
            switch (dfa.getCur_state()){
                case START -> {
                    // 跳过空白符
                    if(cur_char.matches("\\s")){
                        continue;
                    }
                    // 分析关键字或标识符
                    else if(cur_char.matches("[a-zA-Z_]")){
                        this.reader.back();
                        this.read_word();
                    }
                    // 分析数字
                    else if(cur_char.matches("[0-9]")){
                        Tokens.add(Token.normal("IntConst",cur_char));
                    }
                    // 单独处理分号
                    else if(cur_char.equals(";")){
                        Tokens.add(Token.simple("Semicolon"));
                    }
                    // 分析特殊字符
                    else if(cur_char.matches("[=,()+*/-]")) {
                        Tokens.add(Token.simple(cur_char));
                    }
                    // 非法字符报错
                    else{
                        throw new RuntimeException("File '%s', line %d: Illegal character: '%s' ".formatted(filepath,reader.getLine(),cur_char) );
                    }
                }
            }
        }
        // 添加结尾标志
        Tokens.add(Token.eof());
    }

    /**
     * 获得词法分析的结果, 保证在调用了 run 方法之后调用
     *
     * @return Token 列表
     */
    public Iterable<Token> getTokens() {
        return this.Tokens;
    }

    // 把得到的tokens写入文件
    public void dumpTokens(String path) {
        FileUtils.writeLines(
            path,
            StreamSupport.stream(getTokens().spliterator(), false).map(Token::toString).toList()
        );
    }


}
